import * as shlib from 'shlib/browser'

import { ShTerm } from './index'
import { ShElement } from './ShElement'
import { ShRowElement } from './ShRowElement'
import { ShSpanElement } from './ShSpanElement'


export class ShLineElement extends ShElement {

    public static create(autoWrap: boolean): ShLineElement {
        const $line = shlib.createElement('shterm-line') as ShLineElement
        $line.setAutoWrap(autoWrap)
        
        return $line
    }

    private _autoWrap: boolean = false

    public get autoWrap(): boolean { return this._autoWrap }
    public get text(): string { return this.innerText}
    
    constructor() {
        super()
    }

    public setAutoWrap(autoWrap: boolean) {
        this._autoWrap = autoWrap
        this._rewrap()
    }
    
    public appendSpan($span: ShSpanElement) {
        let $row = this.lastElementChild as ShRowElement | null
        if (! $row) {
            $row = ShRowElement.create()
            this.appendChild($row)
        }
        $row.appendSpan($span)

        this._rewrap()
    }

    public updateLayout() {
        for (const $row of this.children) {
            for (const $span of $row.children) {
                ;($span as ShSpanElement).updateLayout()
            }
        }

        this._rewrap()
    }

    /**
     * 如果 autoWrap 为 true，则逐物理行重排，否则，将多行合并为一行。
     */
    private _rewrap() {
        if (this._autoWrap) {
            // 逐物理行重排

            if (this.children.length === 0)
                return

            const $term = this.$term()
            shlib.assertNotNull($term)

            let i = 0
            while (i < this.children.length) {
                const $row = this.children[i] as ShRowElement
                const ncols = $row.countColumns()
                if (ncols > $term.cols) {
                    // 本行太长，需要拆分

                    // 将本行从 $term.cols 开始截断
                    let $lastSpan: ShSpanElement | null = null
                    let $span = $row.breakAt($term.cols)
                    shlib.assertNotNull($span)

                    // 得到下一行
                    let $nextRow = $row.nextElementSibling as ShRowElement | null
                    if (! $nextRow) {
                        $nextRow = ShRowElement.create()
                        this.appendChild($nextRow)
                    }

                    // 将截断后剩余部分添加到下一行头部
                    while ($span !== null) {
                        const $next = $span.nextElementSibling as ShSpanElement | null
                        $span.remove()
                        if ($lastSpan === null)
                            $nextRow.insertAdjacentElement('afterbegin', $span)
                        else
                            $lastSpan.insertAdjacentElement('afterend', $span)

                        $lastSpan = $span
                        $span = $next
                    }

                    $lastSpan!.tryMergeRight()
                }
                else if (ncols < $term.cols) {
                    // 本行短于屏幕宽度

                    let $nextRow = $row.nextElementSibling as ShRowElement | null
                    if (! $nextRow)
                        break

                    // 从下一行 $term.cols - ncols 开始截断
                    const $remainSpan = $nextRow.breakAt($term.cols - ncols)
                    
                    // 将截断出的前半部分添加到本行的尾部
                    let $span = ($nextRow.children[0] as ShSpanElement | undefined) || null
                    while ($span !== $remainSpan) {
                        const $next = $span!.nextElementSibling as ShSpanElement | null
                        $row.appendSpan($span!)
                        $span = $next
                    }

                    if ($nextRow.children.length === 0)
                        $nextRow.remove()
                }

                i++
            }
        }
        else { // this._autoWrap === false
            // 将多个物理行合并为一行
            
            if (this.children.length <= 1)
                return

            const $firstRow = this.children[0] as ShRowElement
            for (let i = 1; i < this.children.length; i++) {
                const $row = this.children[i] as ShRowElement
                for (let j = 0; j < $row.children.length; j++) {
                    const $span = $row.children[j] as ShSpanElement
                    $firstRow.appendSpan($span)
                }
            }
        }
    }
}

customElements.define('shterm-line', ShLineElement)
